/*
 * libroxsd - a portable, lightweight XSD validation library
 * Copyright (C) 2012  Emmanuel Deloget
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

#ifndef roxsd_list_h_included
#define roxsd_list_h_included

typedef struct list_s list_t;
struct list_s {
	struct list_s *prev;
	struct list_s *next;
};

/** @brief Declares a list header
 *
 * Any list data shall contain at least one list header.
 * The header MUST be included at the beginning of any defined list.
 * @code
 * struct my_object_s {
 *   list_header(); // define list header
 *   ... // other fields
 * }
 * @endcode
 */
#define list_define_header() list_t __header

/** @brief Get the list info in the object
 * @param o object
 * @return the contained list object
 */
static inline list_t *__list_fetch(void *o)
{
	return o;
}

/** @brief Get next list node
 * @param node list node
 * @return the next list node
 */
static inline list_t *__list_next(list_t *node)
{
	return node ? node->next : 0;
}

/** @brief Get next list node
 * @param o object
 * @return the next list node
 */
static inline void *list_next(void *o)
{
	return __list_next(__list_fetch(o));
}

/** @brief Get previous list node
 * @param node list node
 * @return the previous list node
 */
static inline list_t *__list_prev(list_t *node)
{
	return node ? node->prev : 0;
}

/** @brief Get previous list node
 * @param o object
 * @return the previous list node
 */
static inline void *list_prev(void *o)
{
	return __list_prev(__list_fetch(o));
}

/** @brief Unlink a node in the list
 * @param head list head
 * @param node node to unlink
 * @return adjusted list head
 */
static inline list_t *__list_unlink(list_t *head, list_t *node)
{
	if (node) {
		if (node->prev) {
			node->prev->next = node->next;
		} else {
			head = node->next;
		}
		if (node->next) {
			node->next->prev = node->prev;
		}
	}
	return head;
}

/** @brief Unlink a node in the list
 * @param ohead head object
 * @param onode object to unlink
 * @return adjusted object head
 */
static inline void *list_unlink(void *ohead, void *onode)
{
	return __list_unlink(__list_fetch(ohead), __list_fetch(onode));
}

/** @brief get list content
 * @param node list node
 * @return the node content
 *
 * Node content is placed right after the list_t header, so we just need to
 * move to the "next" list node: it should be the content address.
 */
static inline void *__list_content(list_t *node)
{
	return node + 1;
}

/** @brief get list content
 * @param node list node
 * @return the node content
 */
static inline void *list_content(void *o)
{
	return __list_content(__list_fetch(o));
}

/** @brief Loop through the list nodes
 * @param n list node
 * @param head list head
 *
 * This macro is useful to implement a list run. It goes through each list
 * node until it finds a NULL. The following code shows how to use it.
 * @code
 * __foreach_inlist(n, my_list) {
 *   data = list_content(n);
 * }
 * @endcode
 */
#define __foreach_inlist(n,head) for(list_t *n=__list_fetch(head); n; n=__list_next(n))

/**
 * @brief Count the list items
 * @param head list head
 * @return the number of list items
 */
static inline unsigned int __list_count(list_t *head)
{
	unsigned int count = 0;

	__foreach_inlist(n, head) {
		++count;
	}

	return count;
}

/**
 * @brief Count the list items
 * @param head list head
 * @return the number of list items
 */
static inline unsigned int list_count(void *o)
{
	return __list_count(__list_fetch(o));
}

/** @brief Adds a link at the start of the list
 * @param head list head
 * @param node node to add to the list
 * @return adjusted list head
 *
 * One must make sure that head is really the list head, otherwise
 * this may break the list.
 */
list_t *__list_pushfront(list_t *head, list_t *node);

/** @brief Adds a link at the start of the list
 * @param ohead head object
 * @param onode object to add to the list
 * @return adjusted object head
 */
static inline void *list_pushfront(void *ohead, void *onode)
{
	return __list_pushfront(__list_fetch(ohead), __list_fetch(onode));
}

/** @brief Adds a link at the start of the list
 * @param head list head
 * @param node node to add to the list
 * @return adjusted list head
 *
 * One must make sure that head is really the list head, otherwise
 * this may break the list.
 */
list_t *__list_pushback(list_t *head, list_t *node);

/** @brief Adds a link at the end of the list
 * @param ohead head object
 * @param onode object to add to the list
 * @return adjusted object head
 */
static inline void *list_pushback(void *ohead, void *onode)
{
	return __list_pushback(__list_fetch(ohead), __list_fetch(onode));
}

#endif /* roxsd_list_h_included */

